package com.zaicheng.cloud.context.named;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.env.MapPropertySource;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 @author zaicheng
 @version 1.0
 @date 2022-04-19
 @email zzc15847256473@163.com
 */

public class NamedContextFactory<C extends NamedContextFactory.Specification> implements DisposableBean, ApplicationContextAware {

    private final  String propertySourceName;

    private final  String propertyName;

    private Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>();

    private Map<String,C> configurations = new ConcurrentHashMap<>();

    private ApplicationContext parent;

    private Class<?> defaultConfigType;

    public NamedContextFactory(String propertySourceName, String propertyName, Class<?> defaultConfigType) {
        this.propertySourceName = propertySourceName;
        this.propertyName = propertyName;
        this.defaultConfigType = defaultConfigType;
    }

    /**
     * 销毁bean
     * @throws Exception
     */
    @Override
    public void destroy() throws Exception {
        // 从容器中获取上下文
        Collection<AnnotationConfigApplicationContext> values = this.contexts.values();

        Iterator<AnnotationConfigApplicationContext> iterator = values.iterator();

        while (iterator.hasNext()){
            AnnotationConfigApplicationContext context = iterator.next();
            // 遍历全部上下文，进行关闭
            context.close();
        }
        // 清空
        this.contexts.clear();
    }

    /**
     * 传入服务名字，获取到注解上下文
     * @param name
     * @return
     */
    public  AnnotationConfigApplicationContext getContext(String name){
        if(!this.contexts.containsKey(name)){
            synchronized (this.contexts){
                if (!this.contexts.containsKey(name)){
                    this.contexts.put(name,this.createContext(name));
                }
            }
        }
        return this.contexts.get(name);
    }

    private AnnotationConfigApplicationContext createContext(String name) {

        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

        if(this.contexts.containsKey(name)){
            Class<?>[] value1 = ((NamedContextFactory.Specification)this.configurations.get(name)).getConfiguration();

            for (int value = 0 ;value < value1.length; ++value){
                Class<?> configuratin = value1[value];
                context.register(new Class[]{configuratin});
            }
        }

        Iterator<Map.Entry<String, C>> iterator = this.configurations.entrySet().iterator();

        while (true){

            Map.Entry entry;

            do {

                if (!iterator.hasNext()){

                    context.register(new Class[]{PropertyPlaceholderAutoConfiguration.class,this.defaultConfigType});
                    context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(this.propertySourceName,Collections.singletonMap(this.propertyName,name)));

                    if (this.parent!=null){
                        context.setParent(this.parent);
                        context.setClassLoader(this.parent.getClassLoader());
                    }

                    context.setDisplayName(this.generateDisplayName(name));
                    context.refresh();
                    return context;
                }



                entry = iterator.next();
            }while (!((String)entry.getKey()).startsWith("default."));


            Class<?>[] value3 = ((Specification) entry.getValue()).getConfiguration();

            for (int value4 = 0; value4 < value3.length; ++value4){
                Class<?> confiuration = value3[value4];
                context.register(new Class[]{confiuration});
            }

        }
    }


    protected  String generateDisplayName(String name){
        return  this.getClass().getSimpleName() + "-" + name;
    }

    //  获取实例对象
    public  <T> T getInstance(String name,Class<T> type){
        AnnotationConfigApplicationContext context = this.getContext(name);
        try {
            return  context.getBean(type);
        }catch (NoSuchBeanDefinitionException e){
            e.printStackTrace();
            return  null;
        }
    }

    /**
     * 获取上下文服务名字
     * @return
     */
    public Set<String> getContextNames(){
        return  new HashSet<>(this.contexts.keySet());
    }


    // 设置spring上下文，在任何地方实现该接口都能获取到
    @Override
    public void setApplicationContext(ApplicationContext parent) throws BeansException {
         this.parent = parent;
    }

    // 设置配置信息
    public  void setConfigurations(List<C> configurations){

        Iterator<C> iterator = configurations.iterator();

        while (iterator.hasNext()){

            C client = iterator.next();

            this.configurations.put(client.getName(),client);
        }

    }







    public interface  Specification{

        String getName();

        Class<?>[] getConfiguration();
    }

}
